import Watcher from "./watcher";

/**
 * 定义MVVM的另一个功能模版编译实现类
 */
export default class Compiler {
    constructor(context) {
        this.$el = context.$el;
        this.context = context;
        if (this.$el) {
            this.$fragment = this.nodeToFragment(this.$el);
            this.compiler(this.$fragment);
            this.$el.appendChild(this.$fragment)
        }
    }

    nodeToFragment(node) {
        let fragment = document.createDocumentFragment();
        if (node.childNodes && node.childNodes.length) {
            node.childNodes.forEach((child) => {
                if (!this.ignorable(child)) {
                    fragment.appendChild(child);
                }
            });
        }
        return fragment;
    }

    ignorable(node) {
        var reg = /^[\t\n\r]+/;
        return (
            node.nodeType === 8 || (node.nodeType === 3 && reg.test(node.textContent))
        )
    }

    compiler(fragment) {
        if (fragment.childNodes && fragment.childNodes.length) {
            fragment.childNodes.forEach((child) => {
                if (child.nodeType === 1) {
                    this.compilerElementNode(child);
                } else if (child.nodeType === 3) {
                    this.compilerTextNode(node);
                }
            })
        }
    }

    compilerElementNode(node) {
        let attrs = [...node.attributes];
        attrs.forEach((attr) => {
            let { name: attrName, value: attrValue } = attr;
            if (attrName.indexOf('v-') === 0) {
                let dirName = attrName.slice(2);
                switch (dirName) {
                    case 'text':
                        new Watcher(attrValue, this.context, (newValue) => {
                            node.textContent = newValue;
                        })
                        break;

                    case 'model':
                        new Watcher(attrName, this.context, (newValue) => {
                            node.value = newValue;
                        })
                        node.addEventListener('input', (e) => {
                            this.context[attrValue] = e.target.value;
                        })
                        break;

                    default:
                        break;
                }
            }
        });
        this.compiler(node);
    }

    compilerTextNode(node) {
        let text = node.textContent.trim();
        if (text) {
            let exp = this.parseTextExp(text);
            new Watcher(exp, this.context, (newValue) => {
                node.textContent = newValue;
            })
        }
    }

    parseTextExp(text) {
        let reg = /\{\{(.+?)\}\}/g;
        var pices = text.split(reg);
        var matches = text.match(reg);
        let tokens = [];
        pices.forEach((item) => {
            if (matches && matches.indexOf(`{{${item}}}`) > -1) {
                tokens.push(`(${item})`);
            } else {
                tokens.push(`\`${item}\``)
            }
        })
        return tokens.join('+')
    }
}